package com.gitee.huanminabc.fastboot.orm.sqlpaginator;

import com.gitee.huanminabc.jcommon.cache.MemoryCache;
import com.gitee.huanminabc.jcommon.container.DataPage;
import com.gitee.huanminabc.jcommon.encryption.HashUtil;
import com.gitee.huanminabc.jcommon.model.DataKeyValue;
import com.gitee.huanminabc.fastboot.orm.exception.ORMException;
import com.gitee.huanminabc.fastboot.orm.DatabaseType;
import com.gitee.huanminabc.fastboot.orm.methods.select.SelectCountMethod;
import com.gitee.huanminabc.jlambda_sql.jdbc.JdbcCommand;
import com.gitee.huanminabc.jlambda_sql.jdbc.JdbcManage;
import com.zaxxer.hikari.HikariDataSource;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * @description:
 * @author: huanmin
 * @create: 2025-04-10 15:08
 **/
public class AbstractPage {
    //缓存用于分页数量,缓存1000条数据,过期时间5分钟
    private static final MemoryCache<DataKeyValue<String, Long>> memoryPageCountCache = new MemoryCache<>(1000, 1000 * 60 * 5);


    public <T> DataPage<T> page(int page, int pageSize, String sql, Class<T> tClass, Object... args) {

        String sqlKey = getSqlKey(sql, args);
        Long count = getPageCount(sql, args, sqlKey);
        sql = limitSql(sql, page, pageSize);
        List<T> pageData = JdbcCommand.selectList(sql, tClass, args);
        //如果查询是空了,并且有缓存那么删除缓存重新查询,这样就补救了,如果查询到空页,返回的总页数对不上的问题
        if (pageData.isEmpty() && memoryPageCountCache.get(sqlKey) != null) {
            memoryPageCountCache.remove(sqlKey);
            count = SelectCountMethod.selectCount(sql, args);
            memoryPageCountCache.put(sqlKey, new DataKeyValue<>(sqlKey, count));
        }
        return new DataPage<T>(Math.toIntExact(count), page, pageSize, pageData);
    }


    public DataPage<Map<String, Object>> page(int page, int pageSize, String sql, Object... args) {
        String sqlKey = getSqlKey(sql, args);
        Long count = getPageCount(sql, args, sqlKey);
        sql = limitSql(sql, page, pageSize);
        List<Map<String, Object>> pageData = JdbcCommand.selectMaps(sql, args);
        //如果查询是空了,并且有缓存那么删除缓存重新查询,这样就补救了,如果查询到空页,返回的总页数对不上的问题
        if (pageData.isEmpty() && memoryPageCountCache.get(sqlKey) != null) {
            memoryPageCountCache.remove(sqlKey);
            //调用真实的统计方法
            count = SelectCountMethod.selectCount(sql, args);
            memoryPageCountCache.put(sqlKey, new DataKeyValue<>(sqlKey, count));
        }
        return new DataPage<>(Math.toIntExact(count), page, pageSize, pageData);
    }


    private Long getPageCount(String sql, Object[] args, String sqlKey) {
        Long count;
        DataKeyValue<String, Long> dataKeyValue = memoryPageCountCache.get(sqlKey);
        if (dataKeyValue != null) {
            count = dataKeyValue.getValue();
        } else {
            count = SelectCountMethod.selectCount(sql, args);
            memoryPageCountCache.put(sqlKey, new DataKeyValue<>(sqlKey, count));
        }
        return count;
    }

    //根据sql和参数进行md5
    private String getSqlKey(String sql, Object... args) {
        StringBuilder sb = new StringBuilder(sql);
        for (Object arg : args) {
            sb.append(arg);
        }
        return HashUtil.md5(sb.toString());
    }

    public static DatabaseType getDatabaseTypeFromUrl(String jdbcUrl) {
        if (jdbcUrl == null || jdbcUrl.trim().isEmpty()) {
            // 保持与原逻辑一致，或改为返回 UNKNOWN
            throw new ORMException("JDBC URL 不能为空");
            // return DatabaseType.UNKNOWN;
        }

        String lowerCaseUrl = jdbcUrl.toLowerCase(); // 转小写以便匹配

        if (lowerCaseUrl.startsWith("jdbc:mysql:")) {
            return DatabaseType.MYSQL;
        } else if (lowerCaseUrl.startsWith("jdbc:oracle:")) {
            return DatabaseType.ORACLE;
        } else if (lowerCaseUrl.startsWith("jdbc:postgresql:")) {
            return DatabaseType.POSTGRESQL;
        } else if (lowerCaseUrl.startsWith("jdbc:sqlserver:")) {
            return DatabaseType.SQLSERVER;
        } else if (lowerCaseUrl.startsWith("jdbc:sqlite:")) {
            return DatabaseType.SQLITE;
        } else {
            // 对于无法识别的 URL，返回 UNKNOWN 或抛出异常
            throw new ORMException("无法识别的 JDBC URL 数据库类型: " + jdbcUrl);
        }
    }

    /**
     * 根据数据库类型为 SQL 添加分页逻辑。
     *
     * @param sql      原始 SQL 语句
     * @param page     页码 (从 1 开始)
     * @param pageSize 每页大小
     * @return 添加了分页逻辑的 SQL 语句
     * @throws IllegalArgumentException      如果 page 或 pageSize 小于 1，或 databaseType 为 null
     * @throws UnsupportedOperationException 如果数据库类型不受支持
     */
    public static String limitSql(String sql, int page, int pageSize) {
        HikariDataSource dataSource = (HikariDataSource) JdbcManage.getDataSource();
        DatabaseType databaseType = getDatabaseTypeFromUrl(dataSource.getJdbcUrl());
        // 1. 输入校验
        if (sql == null || sql.trim().isEmpty()) {
            throw new ORMException("原始 SQL 语句不能为空");
        }
        Objects.requireNonNull(databaseType, "数据库类型不能为空");
        if (page < 1) {
            // 可以选择抛出异常或使用默认值，这里选择更严格的方式
            throw new ORMException("页码必须大于等于 1");
            // page = 1; // 或者使用默认值
        }
        if (pageSize < 1) {
            throw new ORMException("每页大小必须大于等于 1");
            // pageSize = 10; // 或者使用默认值
        }
        // 2. 获取并使用对应的分页策略
        SqlPaginator paginator;
        switch (databaseType) {
            case MYSQL:
            case SQLITE:
                paginator = new MySqlPaginator();
                break;
            case ORACLE:
                //验证sql中是否有order by ,正则表达式忽略大小写
                validateOrderByExists(sql);
                paginator = new OraclePaginator();
                break;
            case POSTGRESQL:
                paginator = new PostgreSqlPaginator();
                break;
            case SQLSERVER:
                //验证sql中是否有order by ,正则表达式忽略大小写
                validateOrderByExists(sql);
                paginator = new SqlServerPaginator();
                break;
            case UNKNOWN: // 对 UNKNOWN 类型明确抛出异常
            default:
                throw new ORMException("不支持的数据库类型: " + databaseType);
        }
        // 3. 计算偏移量 (offset 从 0 开始)
        int offset = (page - 1) * pageSize;
        // 4. 执行分页
        return paginator.paginate(sql, offset, pageSize);
    }

    /**
     * 验证SQL中是否包含ORDER BY子句
     *
     * @param sql 待验证的SQL
     * @throws ORMException 如果SQL中不包含ORDER BY子句
     */
    private static void validateOrderByExists(String sql) {
        // 正则表达式，忽略大小写，匹配ORDER后跟任意数量空白字符，再跟BY
        if (!Pattern.compile("\\bORDER\\s+BY\\b", Pattern.CASE_INSENSITIVE).matcher(sql).find()) {
            throw new ORMException("原始SQL中必须包含一个明确的、可靠的ORDER BY子句");
        }
    }
}
